﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using MessageSystem.Managed.Rpc;

namespace RpcGenerator.AS3
{
    class AS3ShareMessageGenerator
    {
        public static void GenerateShareMessage(MessageFieldDescription description, IMessageSerializer serializer)
        {
            BeginClass(description, serializer);

                WriteFields(description, serializer);            

                BeginRead(description, serializer);
                EndRead(serializer);                

                BeginWrite(description, serializer);
                EndWrite(serializer);

            EndClass(serializer);
        }

        private static void BeginClass(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine(string.Format("public class {0}",description.Type.Name));
            serializer.BeginBlock();
        }

        private static void EndClass(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private static void WriteFields(MessageFieldDescription description, IMessageSerializer serializer)
        {
            foreach (System.Reflection.PropertyInfo pi in description.Type.GetProperties())
            {
                if (pi.GetCustomAttributes(typeof(RpcFieldAttribute), false).Length > 0)
                {
                    serializer.NewLine(string.Format("public var {0}:{1};", pi.Name, RpcGeneratorHelper.GetParameterTypeName(pi.PropertyType)));             
                }                
            }
        }

        private static void BeginRead(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine("public function Read(ns:NetStream):void");
            serializer.BeginBlock();

            ReadObjectMask(description, serializer);

            foreach (MessageFieldMemberDescription mfmd in description.Members)
            {
                ReadField(mfmd, serializer, description);
            }
        }

        private static void EndRead(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private static void ReadObjectMask(MessageFieldDescription description, IMessageSerializer serializer)
        {
            if (description.ObjectCount > 0)
            {
                int MemberMaskSize = description.MemberMaskSize == 64 ? 32 : description.MemberMaskSize;

                int objMaskCount = description.ObjectCount / MemberMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (MemberMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = ns.ReadUInt8();", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = ns.ReadUInt16();", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = ns.ReadUInt32();", i));
                                break;
                            }
                    }
                }
            }
        }

        private static void ReadField(MessageFieldMemberDescription mfmd, IMessageSerializer serializer, MessageFieldDescription mfd)
        {
            int MemberMaskSize = mfd.MemberMaskSize == 64 ? 32 : mfd.MemberMaskSize;
            switch (mfmd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        ReadPrimitiveType(mfmd.Type, mfmd.Name, serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        int maskIndex = (mfmd.ObjectIndex - 1) / MemberMaskSize;
                        int maskInnerIndex = (mfmd.ObjectIndex - 1) % MemberMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                        serializer.NewLine(string.Format("{0} = new {1}();", mfmd.Name, mfmd.GenericTypeName));
                        serializer.NewLine("var count:int = ns.ReadInt32();");
                        serializer.NewLine("for(var i:int = 0;i<count;++i)");
                        Type genericType = MessageFieldDescription.GetGenericType(mfmd.Type);
                        switch (MessageFieldDescription.GetMessageFieldType(genericType))
                        {
                            case MessageFieldType.PrimitiveType:
                                {
                                    serializer.NewLine(string.Format("var member:{0};", genericType.Name));
                                    ReadPrimitiveType(genericType, "member", serializer);
                                    serializer.NewLine(string.Format("{0}.Add(member);", mfmd.Name));
                                    break;
                                }
                            case MessageFieldType.ClientType:
                                {
                                    serializer.NewLine(string.Format("{0} member = new {0}();", genericType.Name));
                                    serializer.NewLine("member.Read(ns);");
                                    serializer.NewLine(string.Format("{0}.Add(member);", mfmd.Name));
                                    break;
                                }
                            case MessageFieldType.GenericType:
                                {
                                    serializer.NewLine("模板嵌套模板不支持");
                                    break;
                                }
                        }
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        int maskIndex = (mfmd.ObjectIndex - 1) / MemberMaskSize;
                        int maskInnerIndex = (mfmd.ObjectIndex - 1) % MemberMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0} = new {1}();", mfmd.Name, mfmd.Type.Name));
                            serializer.NewLine(string.Format("{0}.Read(ns);", mfmd.Name));
                        serializer.EndBlock();
                        break;
                    }
            }
        }

        private static void ReadPrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt8();", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt8();", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt16();", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt16();", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt32();", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteUInt32();", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteInt64();", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt64();", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadBoolean();", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt32();", name, tp.Name));
                return;
            }
        }

        private static void BeginWrite(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine("public function Write(ns:NetStream):void");
            serializer.BeginBlock();

            WriteObjectMask(description, serializer);

            foreach (MessageFieldMemberDescription mfmd in description.Members)
            {
                WriteField(mfmd, serializer);
            }
        }

        private static void EndWrite(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private static void WriteObjectMask(MessageFieldDescription description, IMessageSerializer serializer)
        {
            if (description.ObjectCount > 0)
            {
                int MemberMaskSize = description.MemberMaskSize == 64 ? 32 : description.MemberMaskSize;

                int objMaskCount = description.ObjectCount / MemberMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (MemberMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = 0;", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = 0;", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = 0;", i));
                                break;
                            }
                    }
                }

                foreach (MessageFieldMemberDescription mfmd in description.Members)
                {
                    if (!mfmd.IsPrimitiveType())
                    {
                        if (mfmd.IsString())
                        {
                            serializer.NewLine(string.Format("if(null == {0} || {0}.length ==0)",mfmd.Name));
                        }
                        else
                        {
                            serializer.NewLine(string.Format("if(null == {0})", mfmd.Name));
                        }

                        serializer.BeginBlock();
                        int maskIndex = (mfmd.ObjectIndex - 1) / MemberMaskSize;
                        int maskInnerIndex = (mfmd.ObjectIndex - 1) % MemberMaskSize;
                        serializer.NewLine(string.Format("objectmask{0} |= {1};", maskIndex, 1 << maskInnerIndex));
                        serializer.EndBlock();
                    }
                }

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (MemberMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt8(objectmask{0});", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt16(objectmask{0});", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt32(objectmask{0});", i));
                                break;
                            }
                    }
                }
            }
        }

        private static void WriteField(MessageFieldMemberDescription mfmd, IMessageSerializer serializer)
        {
            switch (mfmd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        WritePrimitiveType(mfmd.Type, string.Format("{0}", mfmd.Name), serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        serializer.NewLine(string.Format("if(null!={0})", mfmd.Name));
                        serializer.BeginBlock();
                        serializer.NewLine(string.Format("var count:int = {0}.Count;", mfmd.Name));
                        WritePrimitiveType(typeof(Int32), "count", serializer);
                        serializer.NewLine("for(var i:int = 0;i<count;++i)");
                        serializer.BeginBlock();
                        Type genericType = MessageFieldDescription.GetGenericType(mfmd.Type);
                        switch (MessageFieldDescription.GetMessageFieldType(genericType))
                        {
                            case MessageFieldType.PrimitiveType:
                                {
                                    WritePrimitiveType(genericType, string.Format("{0}[i]", mfmd.Name), serializer);
                                    break;
                                }
                            case MessageFieldType.ClientType:
                                {
                                    serializer.NewLine(string.Format("{0}[i].Write(ns);", mfmd.Name));
                                    break;
                                }
                            case MessageFieldType.GenericType:
                                {
                                    serializer.NewLine("模板中嵌套模板不支持");
                                    break;
                                }
                        }
                        serializer.EndBlock();
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        serializer.NewLine(string.Format("if(null!={0})", mfmd.Name));
                        serializer.BeginBlock();
                        serializer.NewLine(string.Format("{0}.Write(ns);", mfmd.Name));
                        serializer.EndBlock();
                        break;
                    }
            }
        }

        private static void WritePrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("ns.WriteInt8({0});", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("ns.WriteUInt8({0});", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("ns.WriteInt16({0});", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("ns.WriteUInt16({0});", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("ns.WriteInt32({0});", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("ns.WriteUInt32({0});", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("ns.WriteInt64({0});", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("ns.WriteUInt64({0});", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("ns.WriteFloat({0});", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("ns.WriteFloat((float){0});", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("ns.WritewBoolean({0});", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("ns.WriteInt32({0});", name));
                return;
            }
        }
    }
}
